package indi.h.mlx.sql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.google.common.base.Optional;

import indi.h.mlx.core.bean.Column;
import indi.h.mlx.sql.inter.SQLJoin;
import indi.h.mlx.sql.inter.SQLParam;
import indi.h.mlx.sql.inter.SQLRule;
import indi.h.mlx.sql.inter.SQLStructure;
import indi.h.mlx.sql.items.SQL;
import indi.h.mlx.sql.items.SQLData;
import indi.h.mlx.sql.util.SQLUtil;
import indi.h.mlx.util.ListUtil;

public class SQLStructureImp implements SQLStructure {
	// 核心表名
	protected String rootTable;

	public void setRootTable(String rootTable) {
		this.rootTable = rootTable;
	}

	// 标识数据
	protected List<SQLData> idData;

	public void setIdData(List<SQLData> idData) {
		this.idData = idData;
	}

	// 根类数据
	protected List<SQLData> rootData;

	public void setRootData(List<SQLData> rootData) {
		this.rootData = rootData;
	}

	// 关联数据
	protected List<SQLData> joinData;

	public void setJoinData(List<SQLData> joinData) {
		this.joinData = joinData;
	}

	// 全部字段数据
	protected List<SQLData> allData;

	public void setAllData(List<SQLData> allData) {
		this.allData = allData;
	}

	// 关联字段名称
	protected Map<String, Column> columnName;

	public void setColumnName(Map<String, Column> columnName) {
		this.columnName = columnName;
	}

	// 关联表名称
	protected Map<String, SQLJoin> tableMap;

	public void setTableMap(Map<String, SQLJoin> tableMap) {
		this.tableMap = tableMap;
	}

	// 创建sql语句头规则
	protected SQLRule<List<SQLData>, SQL> sqlRule;
	// 创建where字句规则
	protected SQLRule<List<SQLData>, SQL> whereRule;

	// 选择的参数
	protected String[] selectParam;

	// 自定义操作
	protected List<SQLParam> whereSqlParam;
	// 分组参数
	protected String groupList;
	// 获取数据条数
	protected String limit;
	// 分组参数
	protected String orderList;

	public SQLStructureImp() {
		this.whereSqlParam = new ArrayList<>();
		this.sqlRule = new CRUDSqlRule();
		this.whereRule = new WhereSqlRule();
	}

	@Override
	public void addParam(SQLData sql, ParamType paramType) {
		// TODO Auto-generated method stub
		Optional.of(sql.getFieldName());
		if (paramType == ParamType.ALL) {
			ListUtil.removeByComparable(this.allData, sql);
			this.allData.add(sql);
		} else if (paramType == ParamType.ID) {
			ListUtil.removeByComparable(this.idData, sql);
			this.idData.add(sql);
		} else if (paramType == ParamType.ROOT) {
			ListUtil.removeByComparable(this.rootData, sql);
			this.rootData.add(sql);
		} else if (paramType == ParamType.JOIN) {
			ListUtil.removeByComparable(this.joinData, sql);
			this.joinData.add(sql);
		}
	}

	@Override
	public void removeParam(String fieldName, ParamType paramType) {
		// TODO Auto-generated method stub
		if (paramType == ParamType.ALL) {
			ListUtil.removeByComparable(this.allData, new SQLData(fieldName));
		} else if (paramType == ParamType.ID) {
			ListUtil.removeByComparable(this.idData, new SQLData(fieldName));
		} else if (paramType == ParamType.ROOT) {
			ListUtil.removeByComparable(this.rootData, new SQLData(fieldName));
		} else if (paramType == ParamType.JOIN) {
			ListUtil.removeByComparable(this.joinData, new SQLData(fieldName));
		}
	}

	@Override
	public SQL splitSql(SqlType sqlType) {
		// TODO Auto-generated method stub
		SQL prefix = Optional.fromNullable(sqlPrefix(sqlType)).or(SQLUtil.sqlJoins());
		SQL where = Optional.fromNullable(sqlWhere(sqlType)).or(SQLUtil.sqlJoins());
		return SQLUtil.sqlJoin(prefix, where);
	}

	private List<SQLData> idJoinToRoot() {
		List<SQLData> or = Optional.fromNullable(this.joinData).or(new ArrayList<>());
		List<SQLData> or2 = Optional.fromNullable(this.rootData).or(new ArrayList<>());
		List<SQLData> list = new ArrayList<>(or2);
		for (SQLData s : or) {
			if (s.getValue() != null) {
				list.add(s);
			}
		}
		list.addAll(this.idData);
		return list;
	}

	private SQL sqlPrefix(SqlType sqlType) {
		Map<String, Object> initData = new HashMap<>();
		initData.put(CRUDSqlRule.SELECT_DATA, this.selectParam);
		initData.put(CRUDSqlRule.TABLE, this.tableMap);
		initData.put(CRUDSqlRule.SQL_TYPE, sqlType);
		this.sqlRule.init(initData);
		return this.sqlRule.rule(idJoinToRoot());
	}

	private SQL sqlWhere(SqlType sqlType) {
		Map<String, Object> initData = new HashMap<>();
		initData.put(WhereSqlRule.C_PARAMS, this.whereSqlParam);
		initData.put(WhereSqlRule.GROUP_BY, this.groupList);
		initData.put(WhereSqlRule.ORDER_BY, this.orderList);
		initData.put(WhereSqlRule.LIMIT, this.limit);
		initData.put(WhereSqlRule.SQL_TYPE, sqlType);
		this.whereRule.init(initData);
		if (sqlType == SqlType.SELECT)
			return this.whereRule.rule(allData);
		if (sqlType == SqlType.DELETE_BY_ALL)
			return this.whereRule.rule(idJoinToRoot());
		if (sqlType == SqlType.DELETE_BY_ID)
			return this.whereRule.rule(idData);
		if (sqlType == SqlType.UPDATE_BY_ID)
			return this.whereRule.rule(idData);
		if (sqlType == SqlType.UPDATE_BY_NONE)
			return this.whereRule.rule(null);
		return null;
	}

	@Override
	public SQLStructure orderBy(String[] column, OrderByType type) {
		// TODO Auto-generated method stub
		if (column == null) {
			this.orderList = null;
			return this;
		}
		StringBuilder sb = new StringBuilder("order by ");
		for (String s : column) {
			sb.append(createSelectParam(s));
			sb.append(",");
		}
		this.orderList = sb.deleteCharAt(sb.length() - 1).append(" ").append(type.toString()).toString();
		return this;
	}

	@Override
	public SQLStructure groupBy(String[] column) {
		// TODO Auto-generated method stub
		if (column == null) {
			this.groupList = null;
			return this;
		}
		StringBuilder sb = new StringBuilder("group by ");
		for (String s : column) {
			sb.append(createSelectParam(s));
			sb.append(",");
		}
		this.groupList = sb.deleteCharAt(sb.length() - 1).toString();
		return this;
	}

	@Override
	public SQLStructure limit(Integer start, Integer length) {
		// TODO Auto-generated method stub
		if (length == null)
			throw new NullPointerException("获取数据长度不能为null");
		if (start == null)
			this.limit = "limit " + length;
		else
			this.limit = "limit " + start + "," + length;
		return this;
	}

	@Override
	public SQLStructure limit(Integer length) {
		// TODO Auto-generated method stub
		limit(null, length);
		return this;
	}

	@Override
	public SQLStructure sum(String[] column) {
		// TODO Auto-generated method stub
		this.selectParam = createSelectParams(column, (s) -> {
			return "sum(" + s + ")";
		});
		return this;
	}

	@Override
	public SQLStructure count(boolean clearColumn) {
		// TODO Auto-generated method stub
		String c = "count(*)";
		if (clearColumn) {
			this.selectParam = new String[] { c };
		} else {
			for (String s : this.selectParam) {
				if (s.equals(c)) {
					return this;
				}
			}
			String[] sp = new String[this.selectParam.length + 1];
			System.arraycopy(this.selectParam, 0, sp, 0, this.selectParam.length);
			sp[this.selectParam.length] = c;
			this.selectParam = sp;
		}
		return this;
	}

	@Override
	public void setSelectParam(String[] param) {
		// TODO Auto-generated method stub
		this.selectParam = createSelectParams(param, (s) -> {
			return s;
		});
	}

	public String[] createSelectParams(String[] param, Function<String, String> sqlf) {
		String[] ps = new String[param.length];
		int i = 0;
		for (String p : param) {
			ps[i] = sqlf.apply(createSelectParam(p));
			i++;
		}
		return ps;
	}

	private String createSelectParam(String p) {
		String tableName = "`" + getTableName(p) + "`";
		String columnName = "`" + getColumnName(p) + "`";
		if (columnName.equals("``")) {
			try {
				throw new NullPointerException(p + " 参数没有找到对应的字段");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
		if (!tableName.equals("``"))
			return tableName + "." + columnName;
		else
			return columnName;
	}

	@Override
	public SQLStructure and(String[] columns, Object[] datas, String[] matchs) {
		// TODO Auto-generated method stub
		addWhereSqlParam(columns, datas, matchs, "and", "and");
		return this;
	}

	@Override
	public SQLStructure orAnd(String[] columns, Object[] datas, String[] matchs) {
		// TODO Auto-generated method stub
		addWhereSqlParam(columns, datas, matchs, "or", "and");
		return this;
	}

	@Override
	public SQLStructure or(String[] columns, Object[] datas, String[] matchs) {
		// TODO Auto-generated method stub
		addWhereSqlParam(columns, datas, matchs, "or", "or");
		return this;
	}

	@Override
	public SQLStructure andOr(String[] columns, Object[] datas, String[] matchs) {
		// TODO Auto-generated method stub
		addWhereSqlParam(columns, datas, matchs, "and", "or");
		return this;
	}

	public void addWhereSqlParam(String[] columns, Object[] datas, String[] matchs, String outRelation,
			String inRelation) {
		try {
			columnDataCheck(columns, matchs, matchs);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		this.whereSqlParam.add(new WhereSqlParam(getColumnNames(columns), datas, getTableNames(columns), matchs,
				outRelation, inRelation));
	}

	private void columnDataCheck(String[] columns, Object[] datas, String[] matchs) throws Exception {
		if (columns.length != datas.length)
			throw new Exception("行与数据数组长度不一致,添加条件失败");
		if (columns.length != matchs.length)
			throw new Exception("行与匹配符数组长度不一致,添加条件失败");
	}

	private String[] getTableNames(String[] columns) {
		String[] tableName = new String[columns.length];
		int i = 0;
		for (String c : columns) {
			tableName[i] = getTableName(c);
			i++;
		}
		return tableName;
	}

	private String getTableName(String columnFiled) {
		int indexOf = columnFiled.lastIndexOf(".");
		if (indexOf > 0) {
			return this.tableMap.get(columnFiled.substring(0, indexOf)) == null ? ""
					: this.tableMap.get(columnFiled.substring(0, indexOf)).getOtherName();
		} else {
			return this.rootTable;
		}
	}

	private String[] getColumnNames(String[] columns) {
		String[] columnNamse = new String[columns.length];
		int i = 0;
		for (String c : columns) {
			columnNamse[i] = getColumnName(c);
			i++;
		}
		return columnNamse;
	}

	private String getColumnName(String columnFiled) {
		return this.columnName.get(columnFiled) == null ? "" : this.columnName.get(columnFiled).getcColumn().name();
	}

	@Override
	public void clearCustomWhere() {
		// TODO Auto-generated method stub
		this.whereSqlParam = new ArrayList<>();
	}

	@Override
	public void claerWhereEnd() {
		// TODO Auto-generated method stub
		this.groupList = null;
		this.limit = null;
		this.orderList = null;
	}

	@Override
	public void clearSelectParam() {
		// TODO Auto-generated method stub
		this.selectParam = null;
	}

}
